//
//  MultiThreadViewController.m
//  ObjectCProject
//
//  Created by lvfeijun on 2021/5/24.
// 说明： https://www.jianshu.com/p/0db75f5503d9

#import "LockViewController.h"

#import <objc/runtime.h>

#import <objc/objc-sync.h>

#import <pthread.h>

#import <libkern/OSAtomic.h>

@interface LockViewController ()

@end

@implementation LockViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
}

- (NSArray *)getDatas{
    return @[@"NSLock",@"NSConditionLock",@"NSRecursiveLock",@"NSCondition",
             @"synchronized",@"dispatch_semaphore_t",@"mutex",@"OSSpinLock"];
}

-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
    [tableView deselectRowAtIndexPath:indexPath animated:YES];
    
    [self tableViewSelcctedAction:indexPath];
}

#pragma mark 一、锁
-(void)NSLock{
    NSLock *lock = [[NSLock alloc]init];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        //锁住10秒
        [lock lock];
        NSLog(@"线程一");
        sleep(10);
        [lock unlock];
    });
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        sleep(1);
        //上面锁住10秒了，这里只休眠了1秒，解锁失败
        if ([lock tryLock]) {
            NSLog(@"线程二");
            sleep(1);
            [lock unlock];
        }else{
            NSLog(@"测试加锁失败");
        }
    });
}

#pragma mark 二、条件锁
-(void)NSConditionLock{
    //主线程中
    NSConditionLock *lock = [[NSConditionLock alloc] initWithCondition:0];//条件是0
    
    //线程1
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [lock lockWhenCondition:1];//第一步：条件是1，失败跳过
        NSLog(@"线程1");
        sleep(2);
        [lock unlock];
    });
    
    //线程2
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        sleep(5);//第四部：5秒后条件相同，解锁成功
        if ([lock tryLockWhenCondition:0]) {
            NSLog(@"线程2");
            [lock unlockWithCondition:2];
            NSLog(@"线程2解锁成功");
        } else {
            NSLog(@"线程2尝试加锁失败");
        }
    });
    
    //线程3
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        sleep(2);//第二步：2秒后，条件不是0失败
        if ([lock tryLockWhenCondition:2]) {
            NSLog(@"线程3");
            [lock unlock];
            NSLog(@"线程3解锁成功");
        } else {
            NSLog(@"线程3尝试加锁失败");
        }
    });
    
    //线程4
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        sleep(3);//第三步：3秒后，条件不是0失败
        if ([lock tryLockWhenCondition:2]) {
            NSLog(@"线程4");
            [lock unlockWithCondition:1];
            NSLog(@"线程4解锁成功");
        } else {
            NSLog(@"线程4尝试加锁失败");
        }
    });
}

//#import <objc/objc-sync.h>
#pragma mark 三、递归锁
-(void)NSRecursiveLock{
    NSRecursiveLock *lock = [[NSRecursiveLock alloc] init];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        static void (^RecursiveBlock)(int);
        RecursiveBlock = ^(int value) {
                [lock lock];
                if (value > 0) {
                    NSLog(@"value:%d", value);
                    sleep(3);
                    RecursiveBlock(value - 1);
                }
                [lock unlock];
            };
            dispatch_async(dispatch_get_main_queue(), ^{
                NSLog(@"开始睡觉");
                sleep(2);
                NSLog(@"睡了2秒");
                sleep(4);
                NSLog(@"睡了4秒");
            });
            
            NSLog(@"递归一开始");
            RecursiveBlock(5);//开启
            NSLog(@"递归一完成");
            sleep(1);
            RecursiveBlock(3);//等待上一个递归完成解锁后再执行
        });
}

#pragma mark 四、锁判断
-(void)NSCondition{
    __block NSMutableArray *products = [NSMutableArray array];
    NSCondition *condition = [NSCondition new];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"线程一开始");
        [condition lock];
        while (products.count<1) {
             NSLog(@"线程一等待");
            //线程等待
            [condition wait];
        }
        NSLog(@"线程一执行");
        [products removeObjectAtIndex:0];
        [condition unlock];
        NSLog(@"线程一完");
    });
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        sleep(1);
        NSLog(@"线程二");
        [condition lock];
        NSLog(@"线程二执行");
        [products addObject:@1];
        //唤醒等待的线程
        [condition signal];
        [condition unlock];
        NSLog(@"线程二完");
    });
}

#pragma mark 五、synchronized
-(void)synchronized{
    @synchronized (self) {
//some code
    }
    //等价于
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLock *lock = [NSLock new];
        NSLog(@"锁");
        [lock lock];
//some code
        NSLog(@"解锁");
        [lock unlock];
    });
 //等价于
    NSString *test = @"test";
    id synchronizeTarget = (id)test;
    @try {
        NSLog(@"objc_sync_enter");
        objc_sync_enter(synchronizeTarget);
        test = nil;
    } @finally {
        NSLog(@"objc_sync_exit");
        objc_sync_exit(synchronizeTarget);
    }
}

#pragma mark 六、信号量
-(void)dispatch_semaphore_t{
    dispatch_semaphore_t signal = dispatch_semaphore_create(0);
    dispatch_time_t overTime = dispatch_time(DISPATCH_TIME_NOW, 9 * NSEC_PER_SEC);
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            sleep(3);
            NSLog(@"signal");
            dispatch_semaphore_signal(signal);
        });
    NSLog(@"wait");
    dispatch_semaphore_wait(signal, overTime);
    NSLog(@"end");
    
    /* 任务1 */
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        /* 耗时任务1 */
        NSLog(@"任务1开始");
        [NSThread sleepForTimeInterval:3];
        NSLog(@"任务1结束");
        /* 任务1结束，发送信号告诉任务2可以开始了 */
        dispatch_semaphore_signal(signal);
    });

    /* 任务2 */
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        /* 等待任务1结束获得信号量, 无限等待 */
        dispatch_semaphore_wait(signal, DISPATCH_TIME_FOREVER);
        /* 如果获得信号量则开始任务2 */
        NSLog(@"任务2开始");
        [NSThread sleepForTimeInterval:3];
        NSLog(@"任务2结束");
    });
    [NSThread sleepForTimeInterval:10];
}

#pragma mark 七、互坼锁
static pthread_mutex_t theLock;
//#import <objc/objc-sync.h>
//#import <pthread.h>
- (void)mutex{
/*
PTHREAD_MUTEX_NORMAL 缺省类型，也就是普通锁。当一个线程加锁以后，其余请求锁的线程将形成一个等待队列，并在解锁后先进先出原则获得锁。

PTHREAD_MUTEX_ERRORCHECK 检错锁，如果同一个线程请求同一个锁，则返回 EDEADLK，否则与普通锁类型动作相同。这样就保证当不允许多次加锁时不会出现嵌套情况下的死锁。

PTHREAD_MUTEX_RECURSIVE 递归锁，允许同一个线程对同一个锁成功获得多次，并通过多次 unlock 解锁。

PTHREAD_MUTEX_DEFAULT 适应锁，动作最简单的锁类型，仅等待解锁后重新竞争，没有等待队列。
*/
    pthread_mutex_init(&theLock, NULL);
    
//设置类型
    pthread_mutexattr_t attr;
    pthread_mutexattr_init(&attr);
    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
    pthread_mutex_init(&theLock, &attr);
    pthread_mutexattr_destroy(&attr);

    pthread_t thread;
    pthread_create(&thread, NULL, threadMethord1, NULL);
    
    pthread_t thread2;
    pthread_create(&thread2, NULL, threadMethord2, NULL);
}
void *threadMethord1() {
    pthread_mutex_lock(&theLock);
    printf("线程1\n");
    sleep(2);
    pthread_mutex_unlock(&theLock);
    printf("线程1解锁成功\n");
    return 0;
}
void *threadMethord2() {
    sleep(1);
    pthread_mutex_lock(&theLock);
    printf("线程2\n");
    pthread_mutex_unlock(&theLock);
    printf("线程2解锁成功\n");
    return 0;
}

#pragma mark 八、自旋锁
-(void)OSSpinLock{
//#import <libkern/OSAtomic.h>
    __block OSSpinLock oslock = OS_SPINLOCK_INIT;
    //线程1
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"线程1 准备上锁");
        OSSpinLockLock(&oslock);
        sleep(4);
        NSLog(@"线程1");
        OSSpinLockUnlock(&oslock);
        NSLog(@"线程1 解锁成功");
        NSLog(@"--------------------------------------------------------");
    });

    //线程2
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"线程2 准备上锁");
        OSSpinLockLock(&oslock);
        NSLog(@"线程2");
        OSSpinLockUnlock(&oslock);
        NSLog(@"线程2 解锁成功");
    });
}

/*
#pragma mark - Navigation

// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    // Get the new view controller using [segue destinationViewController].
    // Pass the selected object to the new view controller.
}
*/

@end

